/**
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain an copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.littlejie.circleprogress;

import com.littlejie.circleprogress.utils.*;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.*;
import ohos.agp.utils.*;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

/**
 * 带有刻度的圆形进度条
 *
 * @author fuchi
 * @since 2021-04-02
 */

public class DialProgress extends Component implements Component.DrawTask, Component.EstimateSizeListener {
    static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201, "MY_Dial");
    private static final String TAG = DialProgress.class.getSimpleName();
    private Context mContext;

    // 圆心坐标
    private Point mCenterPoint;
    private float mRadius;
    private float mTextOffsetPercentInRadius;

    private boolean antiAlias;
    // 绘制提示
    private Paint mHintPaint;
    private CharSequence mHint;
    private Color mHintColor;
    private int mHintSize;
    private float mHintOffset;

    // 绘制数值
    private Paint mValuePaint;
    private Color mValueColor;
    private float mMaxValue;
    private float mValue;
    private int mValueSize;
    private float mValueOffset;
    private String mPrecisionFormat;

    // 绘制单位
    private Paint mUnitPaint;
    private int mUnitSize;
    private Color mUnitColor;
    private float mUnitOffset;
    private CharSequence mUnit;
    // 前景圆弧
    private Paint mArcPaint;
    private float mArcWidth;
    private int mDialIntervalDegree;
    private float mStartAngle = 135, mSweepAngle = 270;
    private RectFloat mRectF;
    // 渐变
    private int[] mGradientColors = {ResourceTable.Color_Black, ResourceTable.Color_Green, ResourceTable.Color_Red};
    private Color[] mGradientColorList = {Color.GREEN, Color.YELLOW, Color.RED};
    // 当前进度，[0.0f,1.0f]
    private float mPercent = 0;
    // 动画时间
    private long mAnimTime;
    // 属性动画
    private AnimatorValue mAnimator;

    // 背景圆弧
    private Paint mBgArcPaint;
    private Color mBgArcColor;

    // 刻度线颜色
    private Paint mDialPaint;
    private float mDialWidth;
    private Color mDialColor;

    private int mDefaultSize;

    public DialProgress(Context context, AttrSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    private void init(Context context, AttrSet attrs) {
        mContext = context;
        mDefaultSize = (int) ViewUtils.getInstance().vp2Px(context, Constant.DEFAULT_SIZE);
        mRectF = new RectFloat();
        mCenterPoint = new Point();
        initAttrs(attrs);
        initPaint();
        setValue(mValue);
    }

    private void initAttrs(AttrSet attrSet) {
        antiAlias = ViewUtils.getInstance().getAttrBoolValue(attrSet, Constant.ROUNDPROGRESS_ANTIALIAS, Constant.ANTI_ALIAS);
        mAnimTime = ViewUtils.getInstance().getAttrIntValue(attrSet, Constant.ROUNDPROGRESS_ANIMTIME, Constant.DEFAULT_ANIM_TIME);

        mValue = ViewUtils.getInstance().getFloatValue(attrSet, Constant.ROUNDPROGRESS_VALUE, Constant.DEFAULT_VALUE);
        mValueSize = ViewUtils.getInstance().getDimensionValue(attrSet, Constant.ROUNDPROGRESS_VALUESIZE, Constant.DEFAULT_VALUE_SIZE);
        mValueColor = ViewUtils.getInstance().getAttrColorValue(attrSet, Constant.ROUNDPROGRESS_VALUECOLOR, Color.BLACK);
        mMaxValue = ViewUtils.getInstance().getFloatValue(attrSet, Constant.ROUNDPROGRESS_MAXVALUE, Constant.DEFAULT_MAX_VALUE);

        mHint = ViewUtils.getInstance().getAttrStringValue(attrSet, Constant.ROUNDPROGRESS_HINT, "");
        mHintColor = ViewUtils.getInstance().getAttrColorValue(attrSet, Constant.ROUNDPROGRESS_HINTCOLOR, Color.BLACK);
        mHintSize = ViewUtils.getInstance().getAttrIntValue(attrSet, Constant.ROUNDPROGRESS_HINTSIZE, Constant.DEFAULT_HINT_SIZE);

        mUnit = ViewUtils.getInstance().getAttrStringValue(attrSet, Constant.ROUNDPROGRESS_UNIT, "");
        mUnitColor = ViewUtils.getInstance().getAttrColorValue(attrSet, Constant.ROUNDPROGRESS_UNITCOLOR, Color.BLACK);
        mUnitSize = ViewUtils.getInstance().getDimensionValue(attrSet, Constant.ROUNDPROGRESS_UNITSIZE, Constant.DEFAULT_UNIT_SIZE);

        // 内容数值精度格式
        int mPrecision = ViewUtils.getInstance().getAttrIntValue(attrSet, Constant.ROUNDPROGRESS_PRECISION, 0);
        mPrecisionFormat = MiscUtil.getPrecisionFormat(mPrecision);

        mDialIntervalDegree = ViewUtils.getInstance().getAttrIntValue(attrSet, Constant.ROUNDPROGRESS_DIALINTERVALDEGREE, 10);

        mArcWidth = ViewUtils.getInstance().getDimensionValue(attrSet, Constant.ROUNDPROGRESS_ARCWIDTH, Constant.DEFAULT_ARC_WIDTH);

        mBgArcColor = ViewUtils.getInstance().getAttrColorValue(attrSet, Constant.ROUNDPROGRESS_BGARCCOLOR, Color.RED);
        mTextOffsetPercentInRadius = ViewUtils.getInstance().getFloatValue(attrSet, Constant.ROUNDPROGRESS_TEXTOFFSETPERCENTINRADIUS, 0.33f);

        mDialWidth = ViewUtils.getInstance().getDimensionValue(attrSet, Constant.ROUNDPROGRESS_DIALWIDTH, 0);
        mDialColor = ViewUtils.getInstance().getAttrColorValue(attrSet, Constant.ROUNDPROGRESS_DIALCOLOR, Color.WHITE);


    }

    private void initPaint() {
        mHintPaint = new Paint();
        // 设置抗锯齿,会消耗较大资源，绘制图形速度会变慢。
        mHintPaint.setAntiAlias(antiAlias);
        // 设置绘制文字大小
        mHintPaint.setTextSize((int) mHintSize);
        // 设置画笔颜色
        mHintPaint.setColor(mHintColor);
        // 从中间向两边绘制，不需要再次计算文字
        mHintPaint.setTextAlign(TextAlignment.CENTER);

        mValuePaint = new Paint();
        mValuePaint.setAntiAlias(antiAlias);
        mValuePaint.setTextSize(mValueSize);
        mValuePaint.setColor(mValueColor);
        mValuePaint.setFakeBoldText(true);
        mValuePaint.setTextAlign(TextAlignment.CENTER);

        mUnitPaint = new Paint();
        mUnitPaint.setAntiAlias(antiAlias);
        mUnitPaint.setTextSize(mUnitSize);
        mUnitPaint.setColor(mUnitColor);
        mUnitPaint.setTextAlign(TextAlignment.CENTER);

        mArcPaint = new Paint();
        mArcPaint.setAntiAlias(antiAlias);
        mArcPaint.setStyle(Paint.Style.STROKE_STYLE);
        mArcPaint.setStrokeWidth(mArcWidth);
        mArcPaint.setStrokeCap(Paint.StrokeCap.BUTT_CAP);

        mBgArcPaint = new Paint();
        mBgArcPaint.setAntiAlias(antiAlias);
        mBgArcPaint.setStyle(Paint.Style.STROKE_STYLE);
        mBgArcPaint.setStrokeWidth(mArcWidth);
        mBgArcPaint.setStrokeCap(Paint.StrokeCap.BUTT_CAP);
        mBgArcPaint.setColor(Color.GRAY);

        mDialPaint = new Paint();
        mDialPaint.setAntiAlias(antiAlias);
        mDialPaint.setColor(mDialColor);
        mDialPaint.setStrokeWidth(mDialWidth);
    }

    /**
     * 更新圆弧画笔
     */
    private void updateArcPaint(int[] setColor) {
        mGradientColors = setColor;
        float[] color = {0, 1f};
        Point[] listPoint = {mCenterPoint};
        // 设置渐变
        // 渐变的颜色是360度，如果只显示270，那么则会缺失部分颜色
        LinearShader linearShader = new LinearShader(listPoint, color, mGradientColorList, Shader.TileMode.CLAMP_TILEMODE);
        mArcPaint.setShader(linearShader, Paint.ShaderType.PIXELMAP_SHADER);
    }

    /**
     * 同onMeasure方法
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     * @return boolean
     */
    @Override
    public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
        setEstimatedSize(MiscUtil.measure(widthMeasureSpec, mDefaultSize),
                MiscUtil.measure(heightMeasureSpec, mDefaultSize));
        return false;
    }

    /**
     * 初始化坐标点
     *
     * @param component
     */
    public void initPoint(Component component) {
        int minSize = Math.min(component.getWidth() - component.getPaddingLeft() - component.getPaddingRight() - 2 * (int) mArcWidth,
                component.getHeight() - component.getPaddingTop() - component.getPaddingBottom() - 2 * (int) mArcWidth);
        mRadius = (float) minSize / 2;
        mCenterPoint = new Point((float) component.getWidth() / 2, (float) component.getHeight() / 2);
        float halfWidth = mArcWidth / 2;
        // 绘制圆弧的边界
        mRectF.left = DecimalUtils.subMore(mCenterPoint.getPointX(), mRadius, halfWidth);
        mRectF.top = DecimalUtils.subMore(mCenterPoint.getPointY(), mRadius, halfWidth);
        mRectF.right = DecimalUtils.addMore(mCenterPoint.getPointX(), mRadius, halfWidth);
        mRectF.bottom = DecimalUtils.addMore(mCenterPoint.getPointY(), mRadius, halfWidth);

        mValueOffset = DecimalUtils.add(mCenterPoint.getPointY(), getBaselineOffsetFromY(mValuePaint));
        mHintOffset = DecimalUtils.add(DecimalUtils.sub(mCenterPoint.getPointY(), DecimalUtils.mul(mRadius, mTextOffsetPercentInRadius)), getBaselineOffsetFromY(mHintPaint));
        mUnitOffset = DecimalUtils.addMore(mCenterPoint.getPointY(), DecimalUtils.mul(mRadius, mTextOffsetPercentInRadius), getBaselineOffsetFromY(mUnitPaint));

        updateArcPaint(mGradientColors);
        HiLog.info(LABEL, "onMeasure: 控件大小 = " + "(" + component.getWidth() + ", " + component.getHeight() + ")"
                + ";圆心坐标 = " + mCenterPoint.toString()
                + ";圆半径 = " + mRadius
                + ";圆的外接矩形 = " + mRectF.toString());
    }

    private float getBaselineOffsetFromY(Paint paint) {
        return (float) MiscUtil.measureTextHeight(paint) / 2;
    }

    private void drawArc(Canvas canvas) {
        float currentAngle = mSweepAngle * mPercent;
        // 绘制背景圆弧
        canvas.save();
        canvas.rotate(mStartAngle, mCenterPoint.getPointX(), mCenterPoint.getPointY());
        Arc bgArc = new Arc();
        bgArc.setArc(currentAngle, DecimalUtils.sub(mSweepAngle, currentAngle), false);
        canvas.drawArc(mRectF, bgArc, mBgArcPaint);

//        Arc arc = new Arc();
//        arc.setArc(0, currentAngle, false);
//        canvas.drawArc(mRectF, arc, mArcPaint);
//        canvas.restore();
        updateArcPaint(canvas);
        exPercent = mPercent;
    }

    /**
     * 画扇形进度条
     *
     * @param canvas
     */
    private void updateArcPaint(Canvas canvas) {
        float angle = mPercent * 270;
        float[] color = {0.1f, 0.7f};
        Point[] pointsL = new Point[]{new Point(DecimalUtils.add(mCenterPoint.getPointX(), 50),
                DecimalUtils.add(mCenterPoint.getPointY(), mRadius)),
                new Point(DecimalUtils.sub(mCenterPoint.getPointX(), mRadius),
                        DecimalUtils.sub(mCenterPoint.getPointY(), mRadius))};

        LinearShader linearShaderL = new LinearShader(pointsL, color, mGradientColorList, Shader.TileMode.CLAMP_TILEMODE);

        mArcPaint.setShader(linearShaderL, Paint.ShaderType.LINEAR_SHADER);
        Arc arc = new Arc();
        arc.setArc(0, angle, false);

        canvas.drawArc(mRectF, arc, mArcPaint);
        canvas.restore();
        exPercent = mPercent;

    }

    private void drawDial(Canvas canvas) {
        int total = (int) (mSweepAngle / mDialIntervalDegree);
        canvas.save();
        canvas.rotate(mStartAngle, mCenterPoint.getPointX(), mCenterPoint.getPointY());
        for (int i = 0; i <= total; i++) {
            Point newLinePoint = new Point(DecimalUtils.addMore(mCenterPoint.getPointX(), mRadius, mArcWidth), mCenterPoint.getPointY());
            canvas.drawLine(mCenterPoint, newLinePoint, mDialPaint);
            canvas.rotate(mDialIntervalDegree, mCenterPoint.getPointX(), mCenterPoint.getPointY());
        }
        canvas.restore();
    }

    private void drawText(Canvas canvas) {
//        canvas.drawText(String.format(mPrecisionFormat, mValue), mCenterPoint.getPointX(), mValueOffset, mValuePaint);
        canvas.drawText(mValuePaint, String.format(mPrecisionFormat, mValue), mCenterPoint.getPointX(), mValueOffset);
        if (mHint != null) {
            canvas.drawText(mHintPaint, mHint.toString(), mCenterPoint.getPointX(), mHintOffset);
        }
        if (mUnit != null) {
            canvas.drawText(mUnitPaint, mUnit.toString(), mCenterPoint.getPointX(), mUnitOffset);
        }
    }

    public double getMaxValue() {
        return mMaxValue;
    }

    public void setMaxValue(float maxValue) {
        mMaxValue = maxValue;
    }

    /**
     * 设置当前值
     *
     * @param value
     */
    public void setValue(float value) {
        if (value == 0) {
            mPercent = value;
        }
        if (value > mMaxValue) {
            value = mMaxValue;
        }
        double start = mPercent;
        double end = value / mMaxValue;
        mValue = value;
        startAnimator(start, end, mAnimTime);
    }

    private double exPercent = 0;
    private AnimatorValueUpdateUtil animatorValueUpdateUtil = new AnimatorValueUpdateUtil();

    private void startAnimator(double start, double end, long animTime) {
        mAnimator = new AnimatorValue();
        mAnimator.setDuration(1000);
        mAnimator.setCurveType(Animator.CurveType.SMOOTH_STEP);
        animatorValueUpdateUtil.ofFloat(exPercent, end);
        System.out.println("动画起始值 = " + exPercent + "，动画结束值 = " + end);
        mAnimator.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                mPercent = (float) animatorValueUpdateUtil.getValue(v);
//                System.out.println("动画改变值 : " + mPercent + "--------------------默认值 = " + v);

                invalidate();
            }
        });
        mAnimator.start();
    }

    public int[] getGradientColors() {
        int[] returnData = mGradientColors;
        return returnData;
    }

    public void setGradientColors(int[] gradientColors) {
        updateArcPaint(gradientColors);
    }

    public void reset() {
        startAnimator(mPercent, 0.0f, 1000L);
    }


    /**
     * 此方法加载onDraw方法以重新绘制组件
     */
    @Override
    public void invalidate() {
        super.invalidate();
        addDrawTask(this::onDraw);
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        initPoint(component);
        drawArc(canvas);
        drawDial(canvas);
        drawText(canvas);
    }
}
